package org.ala.tiktools.rpc.kcp.netty;

import java.io.IOException;
import java.util.List;

import org.ala.tiktools.rpc.kcp.exception.KcpRpcOutOfMemeryException;
import org.ala.tiktools.rpc.kcp.protocol.IKcpOutput;
import org.ala.tiktools.rpc.kcp.protocol.Kcp;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator;
import io.netty.channel.Channel;


/**
 * netty层与kcp协议层的一层包装
 * <p>  简化netty调用
 *
 * @author ala
 * @date 2025-04-14 15:44
 */
public class NettyKcp {
    /**
     *  kcp
     */
    protected Kcp kcp;
    /**
     *  快速刷新
     */
    protected boolean fastFlush = false;
    /**
     *  是否合并Segment的缓冲区
     */
    protected boolean mergeSegmentBuf = false;
    /**
     *  上次update时间戳
     */
    protected int tsUpdate;
    /**
     *  是否活跃
     */
    protected boolean active;


    public NettyKcp(int conv, IKcpOutput output) {
        kcp = new Kcp(conv, output);
        active = true;
    }


    /**
     *  是否可以接收数据（rcvQue中是否有数据可以往外读）
     *  <p> 当：rcvQue为空，rcvQue没有找到一个完整消息时，返回false
     */
    public boolean canRecv() {
        return kcp.getReceiver().canRecv();
    }
    /**
     *  rcvQue 中第一个完整消息的字节数
     *  @return -1:queue为空<br/>
     *          -2:queue中还么有收到一个完整消息<br/>
     *          >0 一个完整消息的字节数
     */
    public int peekSize() {
        return kcp.getReceiver().peekSize();
    }
    /**
     *  抽取数据
     *  <p> rcvQue -> buf
     *  <p> 根据rmtWnd大小，rcvBuf -> rcvQue
     */
    public void receive(ByteBuf buf) {
        int r = kcp.recv(buf);
        switch (r) {
            case -3: { throw new KcpRpcOutOfMemeryException("从rcvQue中接收数据，给定的buf容量不够"); }
            default: { break; }
        }
    }
    /**
     *  抽取数据
     *  <p> rcvQue[i].data -> buf[i]
     */
    public void receive(List<ByteBuf> bufs) {
        kcp.recv(bufs);
    }
    /**
     *  buf -> rcvBuf
     *  <p> 等待receive方法抽取数据
     */
    public void input(ByteBuf buf) throws IOException {
        int r = kcp.input(buf);
        switch (r) {
            case -1: { throw new IOException("buf中的数据不够1个kcp头"); }
            case -2: { throw new IOException("非法数据：可读区不够数据段，或数据段长度<0"); }
            case -3: { throw new IOException("非法消息：cmd不可识别"); }
            case -4: { throw new IOException("非法conv：消息中的conv不是发给本conv的"); }
            default: {}
        }
    }


    /**
     *  是否可以发送数据
     *  @param  curCanSend  是否现在发送<br/>
     *                      true：sndBuf + sndQue < sndWnd*2，可发送
     *                      false：sndBuf + sndQue < sndWnd，可发送
     */
    public boolean canSend(boolean curCanSend) {
        return kcp.getSender().canSend(curCanSend);
    }
    /**
     *  buf -> sndQue
     *  <p> 等待flush方法
     */
    public void send(ByteBuf buf) throws IOException {
        int r = kcp.send(buf);
        switch (r) {
            case -1: { throw new IOException("buf没有可写空间"); }
            case -2: { throw new IOException("一个大消息切出的分配数量，超过了frg的表示范围（无符号1子节）"); }
            default: {}
        }
    }
    /**
     *  执行flush()，尝试把消息发送出去（通过kcp绑定的IKcpOutput）
     */
    public int update(int current) {
        kcp.update(current);
        int nextTsUp = kcp.check(current);
        setTsUpdate(nextTsUp);
        return nextTsUp;
    }
    /**
     * 检测发送时间戳
     */
    public int check(int current) {
        return kcp.check(current);
    }
    /**
     * 检测当前是否可flush()
     * <p>  1:ackList不为空（有ack需要回复）<br/>
     *      2:probe不为0（有窗口询问或窗口询问需要回答）<br/>
     *      3:sndBuf不为空，sndQue不为空<br/>
     */
    public boolean checkFlush() {
        return kcp.checkFlush();
    }
    /**
     * ack size
     */
    public int ackSize() { return kcp.getAckList().size(); }
    /**
     * sndQue size
     */
    public int sndQueSize() { return kcp.getSender().getSndQue().size(); }
    /**
     * sndBuf size
     */
    public int sndBufSize() { return kcp.getSender().getSndBuf().size(); }



    /**
     *  设置NoDelay参数
     */
    public void noDelay(boolean noDelay, int interval, int resend, boolean nc) {
        kcp.noDelay(noDelay, interval, resend, nc);
    }
    /**
     *  取远程身份标识
     */
    public int getConv() { return kcp.getConv(); }
    public void setConv(int conv) { kcp.setConv(conv); }

    public boolean isNoDelay() {
        return kcp.getRto().isNoDelay();
    }
    public NettyKcp setNodelay(boolean nodelay) {
        kcp.getRto().setNoDelay(nodelay);
        return this;
    }

    public int getInterval() {
        return kcp.getInterval();
    }
    public NettyKcp setInterval(int interval) {
        kcp.setInterval(interval);
        return this;
    }

    public int getFastResend() {
        return kcp.getFastResend();
    }
    public NettyKcp setFastResend(int fastResend) {
        kcp.setFastResend(fastResend);
        return this;
    }

    public int getFastLimit() { return kcp.getFastLimit(); }
    public NettyKcp setFastLimit(int fastLimit) {
        kcp.setFastLimit(fastLimit);
        return this;
    }

    public boolean isNoCwnd() { return kcp.getCongestion().isNocwnd(); }
    public NettyKcp setNocwnd(boolean nocwnd) {
        kcp.getCongestion().setNocwnd(nocwnd);
        return this;
    }

    public int getMinRto() {
        return kcp.getRto().getRxMinrto();
    }
    public NettyKcp setMinRto(int minRto) {
        kcp.getRto().setRxMinrto(minRto);
        return this;
    }

    public int getMtu() {
        return kcp.getMtu();
    }
    public NettyKcp setMtu(int mtu) {
        kcp.setMtu(mtu);
        return this;
    }

    public boolean isStream() {
        return kcp.isStream();
    }
    public NettyKcp setStream(boolean stream) {
        kcp.setStream(stream);
        return this;
    }

    public int getDeadLink() {
        return kcp.getDeadLink();
    }
    public NettyKcp setDeadLink(int deadLink) {
        kcp.setDeadLink(deadLink);
        return this;
    }

    public int getTsUpdate() {
        return tsUpdate;
    }
    public NettyKcp setTsUpdate(int tsUpdate) {
        this.tsUpdate = tsUpdate;
        return this;
    }

    public NettyKcp setByteBufAllocator(ByteBufAllocator allocator) {
        kcp.setByteBufAllocator(allocator);
        kcp.getSender().setByteBufAllocator(allocator);
        return this;
    }

    public boolean isAutoSetConv() {
        return kcp.isAutoSetConv();
    }
    public NettyKcp setAutoSetConv(boolean autoSetConv) {
        kcp.setAutoSetConv(autoSetConv);
        return this;
    }

    public NettyKcp wndSize(int sndWnd, int rcvWnd) {
        kcp.wndSize(sndWnd, rcvWnd);
        return this;
    }

    public int waitSnd() {
        return kcp.getSender().waitSnd();
    }

    public int getRcvWnd() { return kcp.getReceiver().getRcvWnd(); }
    public NettyKcp setRcvWnd(int rcvWnd) {
        kcp.getReceiver().setRcvWnd(rcvWnd);
        return this;
    }

    public int getSndWnd() { return kcp.getSender().getSndWnd(); }
    public NettyKcp setSndWnd(int sndWnd) {
        kcp.getSender().setSndWnd(sndWnd);
        return this;
    }

    public boolean isFastFlush() {
        return fastFlush;
    }
    public NettyKcp setFastFlush(boolean fastFlush) {
        this.fastFlush = fastFlush;
        return this;
    }

    public boolean isMergeSegmentBuf() {
        return mergeSegmentBuf;
    }
    public NettyKcp setMergeSegmentBuf(boolean mergeSegmentBuf) {
        this.mergeSegmentBuf = mergeSegmentBuf;
        return this;
    }

    public int getState() {
        return kcp.getState();
    }
    public boolean isActive() {
        return active;
    }

    public void setClosed(boolean closeKcp) {
        this.active = false;
        if (closeKcp) {
            setKcpClosed();
        }
    }
    public void setKcpClosed() {
        kcp.setState(-1);
        kcp.release();
    }

    @SuppressWarnings("unchecked")
    public <T extends Channel> T channel() {
        return (T) kcp.getChannel();
    }
    public NettyKcp channel(Channel channel) {
        kcp.setChannel(channel);
        return this;
    }

    public String toString() {
        return String.format("NettyKcp:[conv:%d remote:%s]", getConv(), channel().remoteAddress());
    }

    public void setKcpOutput(IKcpOutput output) {
    	kcp.setKcpOutput(output);
    }
}
